package com.fat;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

public class Utilities 
{
	
	public static double calculateEMA(int N, double todayClosePrice, double yesterdayEMAVal)
	{
		double k = 2.0/(N + 1.0);
		double todayEMAVal = todayClosePrice * k + yesterdayEMAVal * (1.0 - k);
		return todayEMAVal;
	}
	
	public static String determineEMATrend(List<Double> emaList)
	{
		// EMA values are stored in the EMA lists in ascending order (i.e. N=3,N=7,N=40)
		String emaTrend = Constants.EMPTY_STR;
		Double prevEMAVal = null;
		
		for (Double emaVal : emaList)
		{
			// If emaTrend has already been determined to be in transition, then move on
			if (!Constants.TRADE_TYPE_TRANSITION.equals(emaTrend))
			{
				// Initialize prevEMAVal
				if (prevEMAVal == null)
				{
					prevEMAVal = emaVal;
				}
				else if (emaVal < prevEMAVal)
				{
					// Trend is in transition
					if (Constants.TRADE_TYPE_SHORT.equals(emaTrend))
					{
						emaTrend = Constants.TRADE_TYPE_TRANSITION;
					}
					// No trend has been established yet (i.e. second iteration of loop)
					else
					{
						emaTrend = Constants.TRADE_TYPE_LONG;
						prevEMAVal = emaVal;
					}
				}
				else if (emaVal > prevEMAVal)
				{
					// Trend is in transition
					if (Constants.TRADE_TYPE_LONG.equals(emaTrend))
					{
						emaTrend = Constants.TRADE_TYPE_TRANSITION;
					}
					// No trend has been established yet (i.e. second iteration of loop)
					else
					{
						emaTrend = Constants.TRADE_TYPE_SHORT;
						prevEMAVal = emaVal;
					}
				}
			}
		}
		
		return emaTrend;
	}
	
	public static Double determineMaxEmaDiff(List<Double> emaList)
	{
		Double maxEMAVal = emaList.get(0);
		Double minEMAVal = emaList.get(0);
		Double maxEMADiff = 0.0;
		
		for (Double emaVal : emaList)
		{
			maxEMAVal = Math.max(maxEMAVal, emaVal);
			minEMAVal = Math.min(minEMAVal, emaVal);
		}
		
		maxEMADiff = Math.abs(maxEMAVal - minEMAVal) / Constants.PIP_DECIMAL;
		
		return maxEMADiff;
	}
	
	public static Double determineMinMaxEmaDiff(List<Double> emaList)
	{
		Double three = emaList.get(0);
		Double nine = emaList.get(1);
		Double forty = emaList.get(2);
		
		Double max1 = Math.max(three, forty);
		Double max2 = Math.max(nine, forty);
		Double minMax = Math.min(max1, max2);
		
		return minMax;
	}
	
	public static String emaCrossed(DataSet dataSet, Date startDate)
	{		
		String crossed = null;
		
		// EMA crossed--------------------------------------------------
		int emaSampleSize = 11;
		int emaCrossCheckSize = 2;
		String trendDataBlock[] = new String [emaSampleSize];
		Calendar cal = Calendar.getInstance();
		cal.setTime(startDate);
		for (int i = emaSampleSize - 1; i >= 0; i--)
		{
			DataPoint dataPoint = dataSet.getDataPoint(cal.getTime());
			while (dataPoint == null)
			{
				cal.add(Calendar.HOUR_OF_DAY, -1);
				dataPoint = dataSet.getDataPoint(cal.getTime());
			}
			
			List<Double> emaList = dataPoint.getEmaList();
			String emaTrend = determineEMATrend(emaList);
			trendDataBlock[i] = emaTrend;
			cal.add(Calendar.HOUR_OF_DAY, -1);
		}
		
		String leftSideTrend = Constants.EMPTY_STR;
		String prevLSTrend = Constants.EMPTY_STR;
		// Check left side of ema data block
		for (int i = 0; i < emaCrossCheckSize; i++)
		{
			String currTrend = trendDataBlock[i];
			
			if (Constants.EMPTY_STR.equals(prevLSTrend))
			{
				prevLSTrend = currTrend;
				leftSideTrend = currTrend;
			}
			else if (!leftSideTrend.equals(Constants.TRADE_TYPE_TRANSITION))
			{
				if (prevLSTrend.equals(currTrend))
				{
					leftSideTrend = currTrend;
					prevLSTrend = currTrend;
				}
				else
				{
					leftSideTrend = Constants.TRADE_TYPE_TRANSITION;
				}
			}
		}
		
		String rightSideTrend = Constants.EMPTY_STR;
		String prevRSTrend = Constants.EMPTY_STR;
		// Check right side of ema data block
		for (int i = emaSampleSize - emaCrossCheckSize; i < emaSampleSize; i++)
		{
			String currTrend = trendDataBlock[i];
			
			if (Constants.EMPTY_STR.equals(prevRSTrend))
			{
				prevRSTrend = currTrend;
				rightSideTrend = currTrend;
			}
			else if (!rightSideTrend.equals(Constants.TRADE_TYPE_TRANSITION))
			{
				if (prevRSTrend.equals(currTrend))
				{
					rightSideTrend = currTrend;
					prevRSTrend = currTrend;
				}
				else
				{
					rightSideTrend = Constants.TRADE_TYPE_TRANSITION;
				}
			}
		}
		
		if (Constants.TRADE_TYPE_LONG.equals(leftSideTrend) && Constants.TRADE_TYPE_SHORT.equals(rightSideTrend))
		{
			crossed = Constants.TRADE_TYPE_SHORT;
		}
		else if (Constants.TRADE_TYPE_SHORT.equals(leftSideTrend) && Constants.TRADE_TYPE_LONG.equals(rightSideTrend))
		{
			crossed = Constants.TRADE_TYPE_LONG;
		}
		else
		{
			crossed = null;
		}
		
		// Linear Regression--------------------------------------------------
		int linearSampleSize = 25;
		Double linearDataBlock[] = new Double [linearSampleSize];
		cal.setTime(startDate);
		for (int i = linearSampleSize - 1; i >= 0; i--)
		{
			DataPoint dataPoint = dataSet.getDataPoint(cal.getTime());
			while (dataPoint == null)
			{
				cal.add(Calendar.HOUR_OF_DAY, -1);
				dataPoint = dataSet.getDataPoint(cal.getTime());
			}
			
			linearDataBlock[i] = dataPoint.getCloseBid();
			cal.add(Calendar.HOUR_OF_DAY, -1);
		}

		Double slope = calculateLeastSquaresSlope(linearDataBlock);
		
//		if (Math.abs(slope/0.0001) < 2)
//		{
//			crossed = null;
//		}
		
		cal.setTime(startDate);
		
		return crossed;
	}
	
	public static Double calculateLeastSquaresSlope(Double dataBlock[])
	{
		double sumX = 0.0;
		double sumX2 = 0.0;
		double sumY = 0.0;
	
		for (int j = 0; j < dataBlock.length; j++)
		{
			int xi = j;
			sumX += xi;
			sumX2 += xi * xi;
			sumY += dataBlock[j];
		}
		
		double n = dataBlock.length;
		double xbar = sumX / n;
        double ybar = sumY / n;

        // second pass: compute summary statistics
        double xxbar = 0.0, yybar = 0.0, xybar = 0.0;
        for (int j = 0; j < n; j++) 
        {
        	double dataVal = dataBlock[j];
        	int xi = j;
            xxbar += (xi - xbar) * (xi - xbar);
            yybar += (dataVal - ybar) * (dataVal - ybar);
            xybar += (xi - xbar) * (dataVal - ybar);
        }
        double beta1 = xybar / xxbar;
        double beta0 = ybar - beta1 * xbar;
        
        return beta1;
	}
}
